package com.example.javatool.recallfile;

import com.hankcs.hanlp.HanLP;
import okhttp3.*;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class StringSimilarityAnalyzer {
    public static void main(String[] args) {
        // 定义文件路径（根据实际情况修改）
        String aFilePath = "/Users/luoquanwei/gitee_code/java-tool/src/test/java/com/example/javatool/recallfile/A.txt"; // 文档A路径（每行1列）
        String bFilePath = "/Users/luoquanwei/gitee_code/java-tool/src/test/java/com/example/javatool/recallfile/B.txt"; // 文档B路径（每行2列）
        String outputFilePath = "/Users/luoquanwei/gitee_code/java-tool/src/test/java/com/example/javatool/recallfile/similarity_result.txt";

        try {
            // 读取 output.txt 文件数据并计算相似度
            analyzeAndSaveSimilarity(aFilePath, bFilePath, outputFilePath);
            System.out.println("相似度分析完成，结果已保存到 " + outputFilePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取输入文件，计算每行字符串的相似度并保存到输出文件
     *
     * @param aFilePath      输入文件路径
     * @param bFilePath      输入文件路径
     * @param outputFilePath 输出文件路径
     * @throws IOException 读写文件时可能抛出的异常
     */
    private static void analyzeAndSaveSimilarity(String aFilePath, String bFilePath, String outputFilePath) throws IOException {
        // 读取文档A数据（每行1列）
        List<String> aDataList = readFileSingleColumn(aFilePath);
        // 读取文档B数据（每行2列）
        List<String[]> bDataList = readFileTwoColumns(bFilePath);

        // 存储所有对比结果（A数据，B数据第一列，相似度）
        List<SimilarityResult> results = new ArrayList<>();

        // A中每个数据与B中每个数据的第一列循环对比
        for (String aValue : aDataList) {
            for (String[] bParts : bDataList) {
                String bValue = bParts[0]; // 取B的第一列数据对比
                //todo preprocessString 提取汉字方法
                double similarity = callSimilarityApi3(aValue, bValue); // 调用相似度计算方法
                results.add(new SimilarityResult(0,aValue, bValue, similarity));
            }
        }

        // 按相似度从高到低排序
        results.sort((r1, r2) -> Double.compare(r2.similarity, r1.similarity));

        // 保存结果到文件
        saveResultsToFile(results, outputFilePath);
    }

    /**
     * 读取单行单列文件（文档A）
     */
    private static List<String> readFileSingleColumn(String filePath) throws IOException {
        List<String> dataList = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                dataList.add(line.trim()); // 直接存储每行数据（假设无分隔符）
            }
        }
        return dataList;
    }

    /**
     * 读取每行两列的文件（文档B）
     */
    private static List<String[]> readFileTwoColumns(String filePath) throws IOException {
        List<String[]> dataList = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                String[] parts = line.split(" "); // 假设用空格分割两列（根据实际文件分隔符调整）
                if (parts.length >= 2) {
                    dataList.add(new String[]{parts[0], parts[1]}); // 存储[第一列, 第二列]
                }
            }
        }
        return dataList;
    }

    /**
     * 保存排序后的结果到文件
     */
    public static void saveResultsToFile(List<SimilarityResult> results, String outputFilePath) throws IOException {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputFilePath))) {
            for (SimilarityResult result : results) {
                // 相似度保留2位小数
                String formattedSimilarity = String.format("%.2f", result.similarity);
                writer.write(result.aValue + " " + result.bValue + " " + formattedSimilarity + "%");
                writer.newLine();
            }
        }
    }

    /**
     * 存储相似度结果的内部类
     */
    public static class SimilarityResult {
        int line;
        String aValue;
        String bValue;
        double similarity;

        public SimilarityResult(int line,String aValue, String bValue, double similarity) {
            this.line = line;
            this.aValue = aValue;
            this.bValue = bValue;
            this.similarity = similarity;
        }
    }

    /**
     * 预处理字符串，提取汉字和后缀名
     *
     * @param str 输入的字符串
     * @return 处理后的字符串
     */
    private static String preprocessString(String str) {
        // 提取汉字
        Pattern chinesePattern = Pattern.compile("[\\u4e00-\\u9fa5]");
        Matcher chineseMatcher = chinesePattern.matcher(str);
        StringBuilder chineseBuilder = new StringBuilder();
        while (chineseMatcher.find()) {
            chineseBuilder.append(chineseMatcher.group());
        }

        // 提取后缀名
        int dotIndex = str.lastIndexOf('.');
        String extension = dotIndex != -1 ? str.substring(dotIndex) : "";

        return chineseBuilder.toString() + extension;
    }

    /**
     * 模拟调用豆包 AI 模型的接口计算字符串相似度
     *
     * @param str1 第一个字符串
     * @param str2 第二个字符串
     * @return 0 到 100 之间的相似度百分比
     */
    private static final String API_KEY = "19f58463-d3f9-4d5b-b651-61110cbbe2d0";
    private static final String API_URL = "https://ark.cn-beijing.volces.com/api/v3/chat/completions";

    /**
     * 调用 OpenAI GPT 模型的接口计算字符串相似度
     *
     * @param str1 第一个字符串
     * @param str2 第二个字符串
     * @return 0 到 100 之间的相似度百分比
     */
    private static String callSimilarityApi(String str1, String str2) {
        OkHttpClient client = new OkHttpClient();

        String prompt = String.format("计算以下两个字符串的余弦相似度，用 0 到 100 之间的数字表示相似结果，只返回数字，不要返回其他值：字符串 1: %s，字符串 2: %s", str1, str2);

        MediaType JSON = MediaType.get("application/json; charset=utf-8");
        String jsonBody = "{\"model\": \"doubao-1.5-pro-32k-250115\", \"messages\": [{\"role\": \"user\", \"content\": \"" + prompt + "\"}]}";
        RequestBody body = RequestBody.create(jsonBody, JSON);

        Request request = new Request.Builder()
                .url(API_URL)
                .header("Authorization", "Bearer " + API_KEY)
                .post(body)
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful() && response.body() != null) {
                String responseData = response.body().string();
                // 简单解析响应，提取相似度数值
                int startIndex = responseData.indexOf("content\":\"") + "content\":\"".length();
                int endIndex = responseData.indexOf("\"", startIndex);
                String similarityStr = responseData.substring(startIndex, endIndex).trim();
                return similarityStr;
            }
        } catch (IOException | NumberFormatException e) {
            e.printStackTrace();
        }
        // 出错时返回 0
        return "0";
    }

    public static final Map<String, List<String>> wordsMap = new HashMap<>();

    /**
     * 计算两个字符串的余弦相似度（需先分词）
     *
     * @param str1 字符串1
     * @param str2 字符串2
     * @return 0-100的相似度百分比
     */
    public static double callSimilarityApi3(String str1, String str2) {
//        str1 = preprocessString(str1);
//        str2 = preprocessString(str2);
        // 示例：使用jieba分词（需添加jieba依赖）
        List<String> words1 = wordsMap.get(str1);
        if (words1 == null) {
            words1 = HanLP.segment(str1).stream().map(i -> i.word).toList(); // 需初始化分词器
            words1 = words1.stream().distinct().collect(Collectors.toList());
            wordsMap.put(str1, words1);
        }
        List<String> words2 = wordsMap.get(str2);
        if (words2 == null) {
            words2 = HanLP.segment(str2).stream().map(i -> i.word).toList(); // 需初始化分词器
            words2 = words2.stream().distinct().collect(Collectors.toList());;
            wordsMap.put(str2, words2);
        }


        // 构建词频向量
        Map<String, Integer> freq1 = getWordFrequency(words1);
        Map<String, Integer> freq2 = getWordFrequency(words2);

        // 计算余弦相似度
        double dotProduct = 0, norm1 = 0, norm2 = 0;
        for (String word : freq1.keySet()) {
            int count1 = freq1.get(word);
            int count2 = freq2.getOrDefault(word, 0);
            dotProduct += count1 * count2;
            norm1 += Math.pow(count1, 2);
        }
        for (String word : freq2.keySet()) {
            norm2 += Math.pow(freq2.get(word), 2);
        }
        norm1 = Math.sqrt(norm1);
        norm2 = Math.sqrt(norm2);

        double similarity = (norm1 * norm2 == 0) ? 0 : (dotProduct / (norm1 * norm2)) * 100;
        return Math.max(0, Math.min(100, similarity)); // 限制在0-100
    }

    /**
     * 过滤重复度较高的词
     *
     * @param words 词列表
     * @return 过滤后的词列表
     */
    private static List<String> filterHighSimilarityWords(List<String> words) {
        final double similarityThreshold = 0.8; // 相似度阈值，可根据需要调整
        List<String> result = new ArrayList<>();
        for (String word : words) {
            boolean isSimilar = false;
            for (String existingWord : result) {
                double similarity = calculateSimilarity(word, existingWord);
                if (similarity >= similarityThreshold) {
                    isSimilar = true;
                    break;
                }
            }
            if (!isSimilar) {
                result.add(word);
            }
        }
        return result;
    }

    /**
     * 计算两个字符串的相似度，使用编辑距离
     *
     * @param s1 第一个字符串
     * @param s2 第二个字符串
     * @return 相似度，范围 0 到 1
     */
    private static double calculateSimilarity(String s1, String s2) {
        int distance = levenshteinDistance(s1, s2);
        int maxLength = Math.max(s1.length(), s2.length());
        return 1.0 - (double) distance / maxLength;
    }

    /**
     * 计算两个字符串的编辑距离
     *
     * @param s1 第一个字符串
     * @param s2 第二个字符串
     * @return 编辑距离
     */
    private static int levenshteinDistance(String s1, String s2) {
        int[][] dp = new int[s1.length() + 1][s2.length() + 1];

        for (int i = 0; i <= s1.length(); i++) {
            for (int j = 0; j <= s2.length(); j++) {
                if (i == 0) {
                    dp[i][j] = j;
                } else if (j == 0) {
                    dp[i][j] = i;
                } else {
                    dp[i][j] = Math.min(
                            Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1),
                            dp[i - 1][j - 1] + (s1.charAt(i - 1) == s2.charAt(j - 1) ? 0 : 1)
                    );
                }
            }
        }
        return dp[s1.length()][s2.length()];
    }

    /**
     * 统计词频
     */
    private static Map<String, Integer> getWordFrequency(List<String> words) {
        Map<String, Integer> freq = new HashMap<>();
        for (String word : words) {
            freq.put(word, freq.getOrDefault(word, 0) + 1);
        }
        return freq;
    }
}
